﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Drawing;
using System.ComponentModel;
//MicroDesigner
using Jc.MicroDesigner.Events;
using Jc.MicroDesigner.Exceptions;
using Jc.MicroDesigner.Drawings;

namespace Jc.MicroDesigner.Objects
{
    /// <summary>
    /// Тип PIN'а
    /// </summary>
    public enum PINType
    {
        /// <summary>
        /// Входной
        /// </summary>
        [Description("Вход")]
        In,

        /// <summary>
        /// Выходной
        /// </summary>
        [Description("Вывод")]
        Out,

        /// <summary>
        /// Другой
        /// </summary>
        [Description("Другой")]
        Other
    }

    /// <summary>
    /// Элемент типа "PIN"
    /// Используется для моделирования контактов элементов 
    /// </summary>
    public sealed class PIN : BaseElement
    {
        /// <summary>
        /// Класс изображение PIN'а
        /// </summary>
        public class PINImage : ElementImage
        {
            /// <summary>
            /// Точка, от которой вести соединения
            /// </summary>
            public Point BindingPoint
            {
                get;
                set;
            }

            /// <summary>
            /// Создать отображение пина
            /// </summary>
            /// <param name="parent">Родительский пин</param>
            public PINImage(BaseElement parent)
                : base(parent)
            { }
        }

        #region Private

        private onRecieveHandler _onRecieve = null;
        private onSendHandler _onSend = null;
        private PIN _FriendPIN = null;
        private CircuitElement _Parent = null;
        private sbyte _Signal = -1;
        private PINType _Type = PINType.Other;

        private sbyte _DefaultSignal = -1;
        private bool _OutputEnabled = true;

        #endregion

        #region Properties

        /// <summary>
        /// Текущий сигнал
        /// </summary>
        public sbyte Signal
        {
            get
            {
                return _Signal;
            }
        }

        /// <summary>
        /// PIN, с которым выполнено соединение
        /// </summary>
        public PIN FriendPIN
        {
            get
            {
                return _FriendPIN;
            }
            set
            {
                _FriendPIN = value;
            }
        }

        /// <summary>
        /// Родительский элемент
        /// </summary>
        public CircuitElement Parent
        {
            get
            {
                return _Parent;
            }
            set
            {
                _Parent = value;
            }
        }

        /// <summary>
        /// Тип PIN'а
        /// </summary>
        public PINType Type
        {
            get
            {
                return _Type;
            }
            set
            {
                _Type = value;
            }
        }

        /// <summary>
        /// Сигнал по-умолчанию
        /// </summary>
        public sbyte DefaultSignal
        {
            get
            {
                return _DefaultSignal;
            }
            set
            {
                _DefaultSignal = value;
            }
        }

        /// <summary>
        /// Разрешение выхода
        /// </summary>
        public bool OutputEnabled
        {
            get { return _OutputEnabled; }
            set { _OutputEnabled = value; }
        }

        #endregion

        /// <summary>
        /// Создать пин для элемента
        /// </summary>
        /// <param name="name">Имя</param>
        /// <param name="parent">Родительский элемент</param>
        public PIN(string name, CircuitElement parent)
            : base(name)
        {
            _Parent = parent;
            _DrawOptions = new PINImage(this);
        }

        /// <summary>
        /// Событие по принятию сигнала
        /// </summary>
        public event onRecieveHandler onRecieve
        {
            add
            {
                _onRecieve += value;
            }
            remove
            {
                _onRecieve -= value;
            }
        }

        /// <summary>
        /// Событие по отправке сигнала
        /// </summary>
        public event onSendHandler onSend
        {
            add
            {
                _onSend += value;
            }
            remove
            {
                _onSend -= value;
            }
        }

        #region Methods

        /// <summary>
        /// Проинициализировать PIN из ветви файла конфигурации
        /// </summary>
        /// <param name="node">Xml</param>
        public void Init(XmlNode node)
        {
            Name = node.Attributes["name"].Value;
            Type = (PINType)Enum.Parse(typeof(PINType), node.Attributes["type"].Value);
            DrawOptions.Left = Convert.ToInt32(node.Attributes["left"].Value);
            DrawOptions.Top = Convert.ToInt32(node.Attributes["top"].Value);
            DrawOptions.Width = Convert.ToInt32(node.Attributes["width"].Value);
            DrawOptions.Height = Convert.ToInt32(node.Attributes["height"].Value);
            (DrawOptions as PINImage).BindingPoint = new Point(Convert.ToInt32(node.Attributes["bindx"].Value),
                                                                Convert.ToInt32(node.Attributes["bindy"].Value));
        }

        /// <summary>
        /// Отправить сигнал
        /// </summary>
        /// <param name="signal">Значение</param>
        public void Send(sbyte signal)
        {
            //Невозможно собрать схему, в которой у пина типа In будет вызван метод Send
            //if (_Type == PINType.In)
            //    throw new IncorrectSignalException(_Parent, this, SignalType.Out);
            _Signal = signal;
            _TicTime++;
            if ((_FriendPIN != null) && _OutputEnabled)
            {
                _FriendPIN.Recieve(signal);
            }
            if (_onSend != null)
                _onSend(this);
        }

        /// <summary>
        /// Принять сигнал
        /// </summary>
        /// <param name="signal">Значение</param>
        public void Recieve(sbyte signal)
        {
            if (_Type == PINType.Out && _OutputEnabled)
                throw new IncorrectSignalException(_Parent, this, SignalType.In);
            _Signal = signal;
            _TicTime++;
            if (_onRecieve != null)
                _onRecieve(this);
        }

        /// <summary>
        /// Разрушить соединение
        /// </summary>
        public void Disconnect()
        {
            if (_FriendPIN != null)
            {
                _FriendPIN._FriendPIN = null;
                _FriendPIN = null;
            }
        }

        /// <summary>
        /// Получить тип PIN'а
        /// </summary>
        /// <returns></returns>
        public string TypeToString()
        {
            return TypeToString(Type);
        }

        /// <summary>
        /// Установить начальное состояние
        /// </summary>
        public override void Refresh()
        {
            _Signal = _DefaultSignal;
        }

        /// <summary>
        /// Отключить все существующие события
        /// </summary>
        public void DropEvents()
        {
            _onRecieve = null;
            _onSend = null;
        }

        #endregion

        #region Static

        /// <summary>
        /// Создать соединение
        /// </summary>
        /// <param name="one">Первый PIN</param>
        /// <param name="two">Второй PIN</param>
        public static void Unite(PIN one, PIN two)
        {
            one._FriendPIN = two;
            two._FriendPIN = one;
        }

        /// <summary>
        /// Соединить с пином
        /// </summary>
        /// <param name="friend">Пин</param>
        public void Connect(PIN friend)
        {
            FriendPIN = friend;
            friend.FriendPIN = this;
        }

        /// <summary>
        /// Получить тип PIN'а
        /// </summary>
        /// <param name="ptype"></param>
        /// <returns></returns>
        public static string TypeToString(PINType ptype)
        {
            string res = "";
            switch (ptype)
            {
                case PINType.In:
                    res = "Вход";
                    break;

                case PINType.Out:
                    res = "Вывод";
                    break;

                case PINType.Other:
                    res = "Другой";
                    break;
            }
            return res;
        }

        #endregion
    }
}